#include "guiterm.hpp"
#include <algorithm>
#include <QResizeEvent>

CharacterView::CharacterView(TerminalEmulator *model, CharacterViewConfig *viewConfig, bool abridged, QWidget *parent)
	:QWidget(parent)
{
	this->model = model;
	this->abridged = abridged;
	this->viewConfig = viewConfig;
	
	sizeFinalized = false;
	QSizePolicy sizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
	setSizePolicy(sizePolicy);
	
	setFocusPolicy(Qt::ClickFocus);
	
	QObject::connect(model, SIGNAL(rectDirtied(int,int,int,int)), this, SLOT(rectDirtied(int,int,int,int)));
	QObject::connect(model, SIGNAL(heightChanged()), this, SLOT(heightChanged()));
	QObject::connect(model, SIGNAL(cursorMoved(int,int)), this, SLOT(cursorMoved(int,int)));
	
	programFinished = false;
	cursorCol = cursorRow = 0;
	cursorVisible = true;
}

void CharacterView::paintEvent(QPaintEvent *event)
{
	QRect rect = event->rect();
	
	int xmin = rect.x()/viewConfig->fontWidth;
	int ymin = rect.y()/viewConfig->fontHeight;
	int width  = (rect.width()+viewConfig->fontWidth-1)/viewConfig->fontWidth;
	int height = (rect.height()+viewConfig->fontHeight-1)/viewConfig->fontHeight;
	
	repaintRegion(xmin, xmin+width-1, ymin, ymin+height-1);
}

void CharacterView::finish()
{
	programFinished = true;
	setFocusPolicy(Qt::NoFocus);
	cursorMoved(-1,-1);
}

bool CharacterView::isAbridged() const
{
	return abridged;
}

void CharacterView::setAbridged(bool abridged)
{
	if(this->abridged == abridged)
		return;
	
	this->abridged = abridged;
	heightChanged();
}

void CharacterView::repaintRegion(int minCol, int maxCol, int minRow, int maxRow)
{
	QRect widgetRect = rect();
	
	QPainter painter(this);
	painter.setFont(viewConfig->font);
	
	int rowStart = widgetRect.x();
	
	for(int yi=minRow; yi<=maxRow; yi++)
	{
		int x = rowStart + minCol*viewConfig->fontWidth;
		int y = widgetRect.y() + viewConfig->fontHeight*yi;
		
		for(int xi=minCol; xi<=maxCol; xi++)
		{
			int ch = model->getChar(xi, yi);
			if(!ch) break;
			
			QString text = QString(QChar(ch));
			//QColor foreColor(0,0,0);
			//QColor backColor(255,255,255);
			QColor foreColor = model->getForeColor(xi, yi);
			QColor backColor = model->getBackColor(xi, yi);
			
			if(cursorVisible && xi==cursorCol && yi==cursorRow)
				std::swap(foreColor, backColor);
			
			painter.fillRect(x, y, viewConfig->fontWidth, viewConfig->fontHeight, QBrush(backColor));
			
			painter.setPen(foreColor);
			painter.drawText(x, y+viewConfig->fontAscent, text);
			
			x += viewConfig->fontWidth;
		}
	}
}

QSize CharacterView::sizeHint() const
{
	int width = model->getWidth();
	int height = model->heightForWidth(width, abridged);
	if(cursorRow >= height) height = cursorRow+1;
	
	return QSize(viewConfig->fontWidth*width, viewConfig->fontHeight*height);
}

void CharacterView::finalizeSize()
{
	if(!sizeFinalized) {
		sizeFinalized = true;
		updateTermSize();
	}
}

void CharacterView::resizeEvent(QResizeEvent *event)
{
	//trace << "CharacterView::resizeEvent ("<<event->size().width()<<"x"<<event->size().height()<<")\n"; //DEBUG
	if(sizeFinalized)
		updateTermSize();
}

void CharacterView::updateTermSize()
{
	int width = size().width();
	int height = size().height();
	
	model->setWidth(width / viewConfig->fontWidth);
	
	// Don't change terminal height in abridged view
	if(!abridged)
		model->setHeight(height / viewConfig->fontHeight);
}

void CharacterView::rectDirtied(int left, int top, int width, int height)
{
	repaint(left*viewConfig->fontWidth, top*viewConfig->fontHeight, width*viewConfig->fontWidth, height*viewConfig->fontHeight);
}

void CharacterView::heightChanged()
{
	updateGeometry();
}

void CharacterView::cursorMoved(int col, int row)
{
	// Same as old location?
	if(col==cursorCol && row==cursorRow)
		return;
	
	// Hiding the cursor
	if(row<0 || col<0) {
		if(cursorVisible) {
			cursorVisible = false;
			cursorRow = -1;
			cursorCol = -1;
			rectDirtied(cursorCol, cursorRow, 1, 1);
		}
		return;
	}
	
	// Moving the cursor
	if(cursorVisible)
	{
		int oldCursorCol = cursorCol;
		int oldCursorRow = cursorRow;
		
		cursorCol = col;
		cursorRow = row;
		
		rectDirtied(col, row, 1, 1);
		rectDirtied(oldCursorCol, oldCursorRow, 1, 1);
	}
	// Showing the cursor
	else
	{
		cursorCol = col;
		cursorRow = row;
		cursorVisible = true;
		rectDirtied(col, row, 1, 1);
	}
}

